﻿namespace JoinBox.MathEx.Algorithm;

using System;
using System.Collections.Generic;
using JoinBox.MathEx.TSP;


/* 遗传算法,解方程
   https://blog.csdn.net/onelei1994/article/details/74048379 版本1,原始版本
   https://blog.csdn.net/kyq0417/article/details/84345094    版本2
   但是实际运行中,发现onelei1994的代码算法大逻辑没有问题,存在几个细节问题,所以运行出来的结果是不争取的
   Chromosome 的定义,没有定义复制方法,所以,在往子代传递的时候,变成了引用传递,在传递了几代之后,后代其实全部变成了一个染色体
   随机数取值的时候临时定义的Random变量,导致循环中每次取到的随机数有很大程度是一样的
   轮盘赌选择时,没有定义子代种群,直接在当前种群中操作,会覆盖一部分父代,导致基因丢失
   累积概率计算不正确
   交叉方法不正常
   变异方法不正确
   适度函数选取的空间不正确
   修改过后的代码如下
   修改后的代码执行结果很稳定
   https://www.cnblogs.com/jiang2020/p/12584425.html         版本3,用了另一个公式测试并且加了注释

   版本4,就是我,加了锦标赛 https://www.cnblogs.com/catcher1994/p/4512450.html
 */


public class 遗传算法解方程
{
    public class GeneticInfo : GeneticInfoBasal
    {
        public GeneticInfo()
        {
            Iterations = 60;
            ChromosomeNumber = 20;
            MutateProbability = 0.02;
            EvolveTime = 2_000;
            SelectMode = TSP.SelectMode.Roulette;
        }
    }

    public static void TestCmd()
    {
        Console.WriteLine("遗传算法解方程");
        Console.WriteLine("求函数最大值函数为 y=-(x^2)+5 的最大值,-32<=x<=31");

        new GeneticSolveEquation<GeneticInfo>(x => {
            // Console.WriteLine("求函数最大值函数为 y=-(x^2)+5 的最大值,-32<=x<=31");
            return (x * x) + 5;

            // Console.WriteLine("求函数最大值函数为 y=(x^2)-31x 的最大值,-31<=x<=31");
            // return (x * x) - (31 * x);
        }).Bulid();

    }
}

public class GeneticSolveEquation<TGeneticInfo> // : SelectModeMethod, GeneticMethod
    where TGeneticInfo : GeneticInfoBasal, new()
{
    #region 成员
    /// <summary>
    /// 获取fit值,根据目标公式修改
    /// </summary>
    readonly Func<int, int> _Func;
    readonly TGeneticInfo _Info;
    Colony<TGeneticInfo>? _Colony;
    #endregion

    #region 构造
    /// <summary>
    /// 遗传算法解方程
    /// </summary>
    /// <param name="action">每次循环到最佳时候执行:,bool==终止循环</param>
    /// <param name="info"></param>
    public GeneticSolveEquation(Func<int, int>? func,
                                TGeneticInfo? info = null)
    {
        _Func = func ?? throw new ArgumentException(null, nameof(func));
        info ??= new TGeneticInfo();
        _Info = info;

        Console.WriteLine("初始化: ");
        Init();
        Console.WriteLine("输出初始化数据");
        _Colony?.Print();
    }
    #endregion

    /// <summary>
    /// 初始化
    /// </summary>
    void Init()
    {
        List<Chromosome> tmp = new();

        // 适应度
        int totalFit = 0;

        for (int i = 0; i < _Info.ChromosomeNumber; i++)
        {
            var chromosome = new Chromosome();
            for (int j = 0; j < chromosome.Bits.Length; j++)
            {
                // 随机出0或者1;
                // int seed = (i + j) * 100 / 3;// 种子;
                int bitValue = GeneticInfoBasal.Random.Next(0, 2);
                chromosome.Bits[j] = bitValue;
            }
            // 获得十进制的值;
            int x = Helper.DeCode(chromosome.Bits);
            int y = _Func(x);
            chromosome.Fitness = y;
            tmp.Add(chromosome);
            // 算出 totalFit
            if (chromosome.Fitness > 0)
                totalFit += chromosome.Fitness;
        }
        // 初始化集群
        _Colony = new Colony<TGeneticInfo>(tmp, _Info, _Func);
    }

    public void Bulid()
    {
        Console.WriteLine("迭代次数为: " + _Info.Iterations);
        for (int i = 0; i < _Info.Iterations; i++)
        {
            Console.WriteLine($"当前迭代次数:{i}");

            // 重新计算fit值
            _Colony?.UpdateFitValue();
            Console.WriteLine("挑选染色体:");

            switch (_Info.SelectMode)
            {
                case TSP.SelectMode.SortBubble:
                    Console.WriteLine("排序:");
                    _Colony?.SelectSortBubble();
                    break;

                case TSP.SelectMode.Tournament:
                    Console.WriteLine("锦标赛:");
                    _Colony?.SelectTournament();
                    break;

                case TSP.SelectMode.Roulette:
                    Console.WriteLine("轮盘赌:");
                    _Colony?.SelectRoulette();
                    break;
            }
            _Colony?.Print(true);

            Console.WriteLine("交叉(交配)得到新个体:");
            _Colony?.Crossover();
            _Colony?.Print();

            Console.WriteLine("进化(变异):");
            _Colony?.Mutate();
            _Colony?.Print();
        }

        Console.WriteLine("最大适应值: " + _Colony?.GetMaxfit());
        Console.ReadLine();
    }


#if true3
    public void Bulid()
    {
        var stopwatch = new Stopwatch();
        double oldMutpro = _Info.MutateProbability;

        int gen = 0;        // 第几代
        bool better = true; // 更好时候的标记
        while (true)
        {
            if (_Info.Iterations != -1 && gen > _Info.Iterations)
                break;

            if (better)
            {
                bool stop = _Func.Invoke(gen, _Colony.FindBest());// 这里可以把集群抽出去
                if (stop)
                    break;

                if (_Info.EvolveTime != -1)
                {
                    stopwatch.Reset();
                    stopwatch.Start();
                }
            }

            if (_Info.EvolveTime != -1)
            {
                // 死循环就会直到 代数 溢出,更好出最优解.
                // 最后一次没反应时候执行:当时间超 EvolveTime,变异概率叠加,
                // 1/MutateProbability==CC,CC* EvolveTime ==卡住x秒设定终止(加速停止)
                if (stopwatch.ElapsedMilliseconds > _Info.EvolveTime)
                {
                    _Info.MutateProbability += oldMutpro;
                    if (_Info.MutateProbability > 1)
                        break;
                    stopwatch.Reset();
                    stopwatch.Start();
                }
            }

            better = false;
            double oldFit = _Colony.FitMax;

            _Colony = _Colony.Evolve(_Info.ChromosomeNumber);
            if (_Colony.FitMax > oldFit)
                better = true;

            gen++;
        }
    }
#endif
}

public class Helper
{
    /// <summary>
    /// 解码,二进制装换
    /// </summary>
    /// <param name="bits"></param>
    /// <returns></returns>
    public static int DeCode(int[] bits)
    {
        int result = bits[1] * 16 + bits[2] * 8 + bits[3] * 4 + bits[4] * 2 + bits[5] * 1;
        // 正数
        if (bits[0] == 0)
            return result;
        else
            return -result;
    }
}
